﻿using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Util;

namespace Linguist.Acoustic.Tiedstate
{
    /// <summary>
    /// Represents a hidden-markov-model. An HMM consists of a unit (context dependent or independent), a transition matrix
    /// from state to state, and a sequence of senones associated with each state. This representation of an HMM is a
    /// specialized left-to-right markov model. No backward transitions are allowed.
    /// </summary>
    class SenoneHMM: IHMM
    {
        private IUnit unit;
        private IUnit baseUnit;
        private SenoneSequence senoneSequence;
        private float[][] transitionMatrix;
        private HMMPosition position;
        private static int objectCount;
        private IHMMState[] hmmStates;


        /**
        /// Constructs an HMM
         *
        /// @param unit             the unit for this HMM
        /// @param senoneSequence   the sequence of senones for this HMM
        /// @param transitionMatrix the state transition matrix
        /// @param position         the position associated with this HMM
         */
        public SenoneHMM(IUnit unit, SenoneSequence senoneSequence,
                         float[][] transitionMatrix, HMMPosition position) 
        {
            this.unit = unit;
            this.senoneSequence = senoneSequence;
            this.transitionMatrix = transitionMatrix;
            this.position = position;
            Utilities.objectTracker("HMM", objectCount++);

            hmmStates = new IHMMState[transitionMatrix.Length];
            for (int i = 0; i < hmmStates.Length; i++)
            {
                hmmStates[i] = new SenoneHMMState(this, i);
            }
            // baseUnit = Unit.getUnit(unit.getName());
            baseUnit = unit.getBaseUnit();
        }


        /**
        /// Gets the  unit associated with this HMM
         *
        /// @return the unit associated with this HMM
         */
        public IUnit getUnit() {
            return unit;
        }


        /**
        /// Gets the  base unit associated with this HMM
         *
        /// @return the unit associated with this HMM
         */
        public IUnit getBaseUnit() {
            return baseUnit;
        }


        /**
        /// Retrieves the hmm state
         *
        /// @param which the state of interest
         */
        public IHMMState getState(int which) 
        {
            return hmmStates[which];
        }


        /**
        /// Returns the order of the HMM
         *
        /// @return the order of the HMM
         */
        // [[[NOTE: this method is probably not explicitly needed since
        // getSenoneSequence.getSenones().length will provide the same
        // value, but this is certainly more convenient and easier to
        // understand
        public int getOrder() {
            return getSenoneSequence().getSenones().Length;
        }


        /**
        /// Returns the SenoneSequence associated with this HMM
         *
        /// @return the sequence of senones associated with this HMM. The length of the sequence is N, where N is the order
        ///         of the HMM. Note that senone sequences may be shared among HMMs.
         */
        // [[ NOTE: the senone sequence may in fact be a sequence of
        // composite senones
        public SenoneSequence getSenoneSequence() {
            return senoneSequence;
        }


        /**
        /// Determines if this HMM is a composite HMM
         *
        /// @return true if this is a composite hmm
         */
        public Boolean isComposite() 
        {
            ISenone[] senones = getSenoneSequence().getSenones();
            foreach (ISenone senone in senones) 
            {
                if (senone is CompositeSenone) 
                {
                    return true;
                }
            }
            return false;
        }


        /**
        /// Returns the transition matrix that determines the state transition probabilities for the matrix. Each entry in
        /// the transition matrix defines the probability of transitioning from one state to the next. For example, the
        /// probability of transitioning from state 1 to state 2 can be determined by accessing transition matrix
        /// element[1][2].
         *
        /// @return the transition matrix (in log domain) of size NxN where N is the order of the HMM
         */
        public float[][] getTransitionMatrix() 
        {
            return transitionMatrix;
        }


        /**
        /// Returns the transition probability between two states.
         *
        /// @param stateFrom the index of the state this transition goes from
        /// @param stateTo   the index of the state this transition goes to
        /// @return the transition probability (in log domain)
         */
        public float getTransitionProbability(int stateFrom, int stateTo) 
        {
            return transitionMatrix[stateFrom][stateTo];
        }


        /**
        /// Retrieves the position of this HMM. Possible
         *
        /// @return the position for this HMM
         */
        public HMMPosition getPosition() 
        {
            return position;
        }


        /**
        /// Determines if this HMM represents a filler unit. A filler unit is speech that is not meaningful such as a cough,
        /// 'um' , 'er', or silence.
         *
        /// @return true if the HMM  represents a filler unit
         */
        public Boolean isFiller() 
        {
            return unit.isFiller();
        }


        /**
        /// Determines if this HMM corresponds to a context dependent unit
         *
        /// @return true if the HMM is context dependent
         */
        public Boolean isContextDependent() 
        {
            return unit.isContextDependent();
        }


        /**
        /// Gets the initial states (with probabilities) for this HMM
         *
        /// @return the set of arcs that transition to the initial states for this HMM
         */
        public IHMMState getInitialState() 
        {
            return getState(0);
        }


        /**
        /// Returns the string representation of this object
         *
        /// @return the string representation
         */
        override
        public String ToString() 
        {
            String name = isComposite() ? "HMM@" : "HMM";
            return name + '(' + unit + "):" + position;
        }


        public int hashCode() 
        {
            return getSenoneSequence().hashCode();
        }


        public Boolean Equals(Object o) 
        {
            if (this == o) {
                return true;
            } 
            else if (o is SenoneHMM) 
            {
                SenoneHMM other = (SenoneHMM) o;
		        return getSenoneSequence().Equals(other.getSenoneSequence());
            }
            return false;
        }
    }
}
